
package com.hockeo.client.ui.base;

import java.util.ArrayList;
import java.util.List;

import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.Record.RecordUpdate;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.RowEditor;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid.ClicksToEdit;
import com.hockeo.client.VHMain;
import com.hockeo.client.model.JSData;
import com.hockeo.client.model.JSData.ChangeType;
import com.hockeo.shared.rpc.classic.RpcVoidCallback;

/**
 * Base class for all editable grids.
 *
 * @version $Id$
 * @author jjanke
 */
public abstract class VHEditorGrid extends VHGrid implements Listener<StoreEvent<JSData>>
{
  private RowEditor<JSData>  d_rowEditor;
  private final List<JSData> d_listRowsToCommit = new ArrayList<JSData>();

  /**
   * Creates a new VHEditorGrid without special configuration.
   */
  protected VHEditorGrid()
  {
    super();
  }

  /**
   * Creates a new VHEditorGrid with specific configuration arguments.
   *
   * @param aobjConfig the configuration arguments to be set
   */
  protected VHEditorGrid( Object[] aobjConfig )
  {
    super( aobjConfig );
  }

  /**
   * Overridden in order to add a row editor as plugin to the customised grid.
   */
  @Override
  protected void customiseGrid( Grid<JSData> grid )
  {
    super.customiseGrid( grid );

    d_rowEditor = new RowEditor<JSData>();
    d_rowEditor.setClicksToEdit( ClicksToEdit.TWO );

    grid.addPlugin( d_rowEditor );

    // add listener for update events to the store (in order to record changes for future
    // synchronisation with the server)
    //
    getStore().addListener( Store.Update, this );
  }

  protected RowEditor<JSData> getRowEditor()
  {
    return d_rowEditor;
  }

  /**
   * Returns the fully qualified name of the type the edited data conforms to.
   */
  protected abstract String getEditedDataType();

  /**
   * Adds a new line and displays the row editor for it.
   */
  public void addLine()
  {
    JSData dataNew = new JSData( getEditedDataType() );

    d_rowEditor.stopEditing( false );
    getStore().insert( dataNew, 0 );
    d_rowEditor.startEditing( getStore().indexOf( dataNew ), true );
  }

  /**
   * Opens the data editor for the given data.
   */
  public void editLine( JSData dataToEdit )
  {
    d_rowEditor.stopEditing( false );
    d_rowEditor.startEditing( getStore().indexOf( dataToEdit ), true );
  }

  /**
   * Deletes the indicated data.
   */
  public void deleteLine( JSData dataToDelete )
  {
    d_rowEditor.stopEditing( false );
    getStore().remove( dataToDelete );

    // save the deleted line in the list of changes so that it is deleted from the
    // server at the next commit
    //
    dataToDelete.setChangeType( ChangeType.DELETED );
    d_listRowsToCommit.add( dataToDelete );
  }

  /**
   * Resets all changes that have been made to the items in the editor since the last
   * commit.
   */
  public void rollbackChanges()
  {
    getStore().rejectChanges();

    // re-establish deleted rows
    //
    for ( JSData data : d_listRowsToCommit )
      if ( data.getChangeType() == ChangeType.DELETED )
        getStore().add( data );

    // TODO sort the store

    // as all changes have been rolled back, we can empty the commit buffer
    //
    d_listRowsToCommit.clear();
  }

  /**
   * Commits all changes that have been made to the items in the editor since the last
   * commit.
   */
  public void commitChanges()
  {
    getStore().commitChanges();

    if ( d_listRowsToCommit.isEmpty() )
      return;

    VHMain.getRpcClassicService().commitData( d_listRowsToCommit, new RpcVoidCallback() );

    // clear the list - all changes have been submitted to the server
    //
    d_listRowsToCommit.clear();
  }

  /**
   * Handles Store.Update events. Makes sure that all changes are internally saved so that
   * it is known which records to submit to the server.
   */
  @Override
  public void handleEvent( StoreEvent<JSData> evt )
  {
    // Handle Store.Update events, but only if the update is committed
    //
    if ( evt.getType() != Store.Update || evt.getOperation() != RecordUpdate.COMMIT )
      return;

    JSData data = evt.getModel();

    if ( !d_listRowsToCommit.contains( data ) )
    {
      data.setChangeType( ChangeType.MODIFIED );
      d_listRowsToCommit.add( data );
    }
  }
}
